package com.gitee.wsl.struct.register

import timber.log.Timber
import kotlin.reflect.KClass


interface Registry {
    fun <T: Any> require(type: KClass<T>): T
    fun <T: Any> requireAll(type: KClass<T>): List<T>
    fun contains(type: KClass<*>): Boolean
    fun <T: Any> optional(type: KClass<T>) = if (contains(type)) require(type) else null
}

interface MutableRegistry: Registry {
    fun <T: Any, I: T> register(type: KClass<T>, implementation: KClass<I>)
    fun <T: Any> register(type: KClass<T>, instance: T)
}

inline fun <reified T: Any> Registry.require() = require(T::class)
inline fun <reified T: Any> Registry.optional() = optional(T::class)
inline fun <reified T: Any> Registry.requireAll() = requireAll(T::class)
inline fun <reified T: Any> MutableRegistry.register(instance: T) = register(T::class, instance)
inline fun <reified T: Any> MutableRegistry.register() = register(T::class, T::class)
inline fun <reified T: Any> MutableRegistry.register(implementation: KClass<out T>) = register(T::class, implementation)

class RegistryException(message: String, cause: Throwable? = null): Exception(message, cause)

@Suppress("UNCHECKED_CAST")
open class SimpleRegistry(val create: (KClass<*>) -> Any): MutableRegistry {

    private val instances = mutableMapOf<KClass<*>, Any>()

    override fun <T: Any, I: T> register(type: KClass<T>, implementation: KClass<I>) = register(type, create(implementation) as T)

    override fun <T: Any> register(type: KClass<T>, instance: T) {
        instances[type] = instance
        if (instance::class != type) instances[instance::class] = instance
    }

    override fun contains(type: KClass<*>) = instances.contains(type)
    override fun <T: Any> optional(type: KClass<T>) = instances[type] as T?
    override fun <T: Any> require(type: KClass<T>) = optional(type) ?: (create(type) as T).also { register(type, it ) }
    override fun <T: Any> requireAll(type: KClass<T>): List<T> = instances.values.filter { type.isInstance(it) }.distinct() as List<T>

}
//
//
//open class SimpleRegistryWithFactory():SimpleRegistry(){
//
//    private val factoryMap = mutableMapOf<KClass<*>, BeanFactory<*>>()
//
//    fun <T: Any> register(type: KClass<T>, factory: BeanFactory<T>){
//        factoryMap[type] = factory
//    }
//
//    fun <T: Any> optional(type: KClass<T>,vararg args:Any): T?{
//        val ret :T? = optional(type) ?: (factoryMap[type] as BeanFactory<T>?)?.instantiate(args)?.also { register(type, it) }
//        return ret
//    }
//}

fun interface BeanFactory<C> {

    fun newInstance(vararg args: Any): C

    fun instantiate(vararg args: Any): C? {
        return try {
            newInstance(args)
        } catch (e: Exception) {
            Timber.e(e)
            null
        }
    }
}